#include "pthread.hpp"
#include "errno.hpp"

// *****************************************************************************
// * Begin PosixWrap namespace
// *****************************************************************************
BEGIN_PSX_NAMESPACE

// *****************************************************************************
// * class ThreadAttr
// *****************************************************************************
ThreadAttr::ThreadAttr()
{
    int res=pthread_attr_init(&data_);
    if (res)
        throw Error(res);
}

ThreadAttr::~ThreadAttr()
{
    int res=pthread_attr_destroy(&data_);
}

ThreadAttr&
    ThreadAttr::setDetachState(int value)
{
    int res=pthread_attr_setdetachstate(&data_,value);
    if (res)
        throw Error(res);
    return *this;
}

int
    ThreadAttr::getDetachState() const
{
        int value;
        int res=pthread_attr_getdetachstate(&data_,&value);
        
    if (res)
        throw Error(res);
    return value;
}

ThreadAttr&
    ThreadAttr::setGuardSize(size_t value)
{
    int res=pthread_attr_setguardsize(&data_,value);
    if (res)
        throw Error(res);
    return *this;
}
size_t
    ThreadAttr::getGuardSize() const
{
        size_t value;
        int res=pthread_attr_getguardsize(&data_,&value);

    if (res)
        throw Error(res);
    return value;
}

ThreadAttr&
    ThreadAttr::setInheritSched(int value)
{
        int res=pthread_attr_setinheritsched(&data_,value);
    if (res)
        throw Error(res);
    return *this;
}

int
    ThreadAttr::getInheritSched() const
{
        int value;
        int res=pthread_attr_getinheritsched(&data_,&value);

    if (res)
        throw Error(res);
    return value;
}

ThreadAttr&
    ThreadAttr::setSchedParam(const SchedParam& value)
{
        int res=pthread_attr_setschedparam(&data_,&value.getSchedParam());
    if (res)
        throw Error(res);
    return *this;
}

void
    ThreadAttr::getSchedParam(SchedParam& value) const
{
        int res=pthread_attr_getschedparam(&data_,&value.getSchedParam());

    if (res)
        throw Error(res);
}

ThreadAttr&
    ThreadAttr::setSchedPolicy(int value)
{
        int res=pthread_attr_setschedpolicy(&data_,value);
    if (res)
        throw Error(res);
    return *this;
}

int
    ThreadAttr::getSchedPolicy() const
{
        int value;
        int res=pthread_attr_getschedpolicy(&data_,&value);

    if (res)
        throw Error(res);
    return value;
}

ThreadAttr&
    ThreadAttr::setScope(int contentionscope)
{
        int res=pthread_attr_setscope(&data_,contentionscope);
    if (res)
        throw Error(res);
    return *this;
}

int
    ThreadAttr::getScope() const
{
        int value;
        int res=pthread_attr_getscope(&data_,&value);

    if (res)
        throw Error(res);
    return value;
}

ThreadAttr&
    ThreadAttr::setStack(void* address, size_t size)
{
        int res=pthread_attr_setstack(&data_,address,size);
    if (res)
        throw Error(res);
    return *this;
}

void
    ThreadAttr::getStack(void*& address, size_t& size) const
{
        int res=pthread_attr_getstack(&data_,&address,&size);

    if (res)
        throw Error(res);
}

ThreadAttr&
    ThreadAttr::setStackSize(size_t value)
{
        int res=pthread_attr_setstacksize(&data_,value);
    if (res)
        throw Error(res);
    return *this;
}

size_t
    ThreadAttr::getStackSize() const
{
        size_t value;
        int res=pthread_attr_getstacksize(&data_,&value);

    if (res)
        throw Error(res);
    return value;
}

::pthread_attr_t&
    ThreadAttr::getThreadAttr()
{
    return data_;
}

const ::pthread_attr_t&
    ThreadAttr::getThreadAttr() const
{
    return data_;
}

// *****************************************************************************
// * class ThreadAttr
// *****************************************************************************
Thread::Thread(ThreadFunction& fun)
{
    int res=pthread_create(&thread_,0,thread_function,&fun);
    if (res!=0)
        throw Error(res);
}

Thread::Thread(const ThreadAttr& attributes, ThreadFunction& fun)
{
    int res=pthread_create(&thread_,&attributes.getThreadAttr(),thread_function,&fun);
    if (res!=0)
        throw Error(res);
}

Thread::~Thread()
{
}
    
void Thread::cancel()
{
    int res=pthread_cancel(thread_);
    if (res!=0)
        throw Error(res);
}

void Thread::detach()
{
    int res=pthread_detach(thread_);
    if (res!=0)
        throw Error(res);
}
void* Thread::join(Thread& thread)
{
    void* thread_result;
    int res=pthread_join(thread.thread_,&thread_result);
    if (res!=0)
        throw Error(res);
    return thread_result;
}

void* Thread::thread_function(void* param)
{
    return static_cast<ThreadFunction*>(param)->thread();
}

// *****************************************************************************
// * class MutexAttr
// *****************************************************************************
MutexAttr::MutexAttr()
{
    int res=pthread_mutexattr_init(&data_);
    if (res!=0)
        throw Error(res);
}

MutexAttr::~MutexAttr()
{
    int res=pthread_mutexattr_destroy(&data_);
}
    
MutexAttr& MutexAttr::setPrioceiling(int value)
{
    int res=pthread_mutexattr_setprioceiling(&data_,value);
    if (res!=0)
        throw Error(res);
    return *this;
}

int MutexAttr::getPrioceiling() const
{
    int value;
    int res=pthread_mutexattr_getprioceiling(&data_,&value);
    if (res!=0)
        throw Error(res);
    return value;
}

MutexAttr& MutexAttr::setProtocol(int value)
{
    int res=pthread_mutexattr_setprotocol(&data_,value);
    if (res!=0)
        throw Error(res);
    return *this;
}

int MutexAttr::getProtocol() const
{
    int value;
    int res=pthread_mutexattr_getprotocol(&data_,&value);
    if (res!=0)
        throw Error(res);
    return value;
}

MutexAttr& MutexAttr::setShared(int value)
{
    int res=pthread_mutexattr_setpshared(&data_,value);
    if (res!=0)
        throw Error(res);
    return *this;
}

int MutexAttr::getShared() const
{
    int value;
    int res=pthread_mutexattr_getpshared(&data_,&value);
    if (res!=0)
        throw Error(res);
    return value;
}

MutexAttr& MutexAttr::setRobust(int value)
{
    int res=pthread_mutexattr_setrobust(&data_,value);
    if (res!=0)
        throw Error(res);
    return *this;
}

int MutexAttr::getRobust() const
{
    int value;
    int res=pthread_mutexattr_getrobust(&data_,&value);
    if (res!=0)
        throw Error(res);
    return value;
}

MutexAttr& MutexAttr::setType(int value)
{
    int res=pthread_mutexattr_settype(&data_,value);
    if (res!=0)
        throw Error(res);
    return *this;
}

int MutexAttr::getType() const
{
    int value;
    int res=pthread_mutexattr_gettype(&data_,&value);
    if (res!=0)
        throw Error(res);
    return value;
}

pthread_mutexattr_t& MutexAttr::getMutexAttr()
{
    return data_;
}

const pthread_mutexattr_t& MutexAttr::getMutexAttr() const
{
    return data_;
}


// *****************************************************************************
// * class Mutex
// *****************************************************************************
Mutex::Mutex()
{
    int res=pthread_mutex_init(&data_,0);
    if (res!=0)
        throw Error(res);
}

Mutex::Mutex(const MutexAttr& attributes)
{
    int res=pthread_mutex_init(&data_,&attributes.getMutexAttr());
    if (res!=0)
        throw Error(res);
}

Mutex::~Mutex()
{
    int res=pthread_mutex_destroy(&data_);
}

void Mutex::lock()
{
    int res=pthread_mutex_lock(&data_);
    if (res!=0)
        throw Error(res);
}

bool Mutex::tryLock()
{
    int res=pthread_mutex_trylock(&data_);
    switch (res)
    {
        case 0:
            return true;
        case EBUSY:
            return false;
        default:
            throw Error(res);
    }
}

void Mutex::unlock()
{
    int res=pthread_mutex_unlock(&data_);
    if (res!=0)
        throw Error(res);
}
    
void Mutex::timedLock(const TimeSpec& abs_time)
{
    int res=pthread_mutex_timedlock(&data_,&abs_time.getTimeSpec());
    if (res!=0)
        throw Error(res);
}
    
int Mutex::setPrioCeiling(int prioceiling)
{
    int old_ceiling;
    int res=pthread_mutex_setprioceiling(&data_,prioceiling,&old_ceiling);
    if (res!=0)
        throw Error(res);
    return old_ceiling;
}

int Mutex::getPrioCeiling() const
{
    int prioceiling;
    int res=pthread_mutex_getprioceiling(&data_,&prioceiling);
    if (res!=0)
        throw Error(res);
    return prioceiling;
}

// *****************************************************************************
// * End PosixWrap namespace
// *****************************************************************************
END_PSX_NAMESPACE
